package com.cf.api.service.impl;

import com.cf.api.dao.PatientGenDao;
import com.cf.api.dao.ReportGenDao;
import com.cf.api.dao.StudyGenDao;
import com.cf.api.service.StudyGenService;
import com.md.common.utils.UUIDUitl;
import com.md.common.utils.date.DateUtil;
import com.md.drcs.dicom.dto.PatientStudyGenResp;
import com.md.drcs.dicom.dto.UserPatientDTO;
import com.md.drcs.dicom.entity.PatientEntity;
import com.md.drcs.dicom.entity.ReportEntity;
import com.md.drcs.dicom.entity.StudyEntity;
import com.md.drcs.dicom.req.StudyCollectGroupReq;
import com.md.drcs.dicom.service.ReportService;
import com.md.drcs.dicom.service.StudyService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.*;

@Service
@Transactional("otherTransactionManager")
public class StudyGenServiceImpl implements StudyGenService {

    @Autowired
    private StudyGenDao studyGenDao;
    @Autowired
    private PatientGenDao patientGenDao;
    @Autowired
    private ReportGenDao reportGenDao;
    @Autowired
    private StudyService studyService;
    @Autowired
    private ReportService reportService;

    private static final List<Integer> PHONE_PREFIX = Arrays.asList(
            130, 131, 132, 134, 135, 137, 138, 139, 147,
            150, 158, 159, 173, 177, 180, 181, 185, 186);

    @Override
    public void generateStudy(LocalDate date, int proportion) {
        int phonePrefixSize = PHONE_PREFIX.size();
        LocalDateTime genBegin = LocalDateTime.of(date, LocalTime.MIN);
        LocalDateTime genEnd = LocalDateTime.of(date, LocalTime.MAX);
        List<PatientStudyGenResp> genDataRecords = studyService.findByStudyDate(genBegin, genEnd); // query how many patients in target date.
        int genCount = (int) (genDataRecords.size() * proportion / 100.0); // calac needs gen patient count
        LocalDate srcDate = LocalDate.now().minusDays(90);
        LocalDateTime begin = LocalDateTime.of(srcDate, LocalTime.MIN);
        LocalDateTime end = LocalDateTime.of(srcDate, LocalTime.MAX);
        List<PatientStudyGenResp> patients = studyService.findByStudyDate(begin, end); // query source patients from 90 days ago
        List<String> firstNames = new ArrayList<>();
        List<String> lastNameChar = new ArrayList<>();
        for (PatientStudyGenResp patient : patients) {
            firstNames.add(StringUtils.substring(patient.getSName(), 0, 1));
            lastNameChar.add(StringUtils.substring(patient.getSName(), patient.getSName().length() - 1));
        }
        int firstNameSize = firstNames.size();
        int lastNameCharSize = lastNameChar.size();
        Random random = new Random();
        Set<Integer> idxCache = new HashSet<>();
        int patientSize = patients.size();
        LocalDateTime recBegin = LocalDateTime.of(date, LocalTime.of(7, 30));
        LocalDateTime tomorrow = LocalDateTime.of(date, LocalTime.MAX);
        for (int i = 0; i < genCount && i < patientSize; i++) {
            int idx, count = 0;
            while (true) {
                count++;
                idx = random.nextInt(patientSize);
                if (!idxCache.contains(idx)) {
                    idxCache.add(idx);
                    break;
                }
                if (count > 1000) {
                    idx = -1;
                    break;
                }
            }
            if (idx == -1 || idx >= patientSize)
                continue;
            PatientStudyGenResp resp = patients.get(idx);
            PatientEntity patient = new PatientEntity();
            BeanUtils.copyProperties(resp, patient);
            StringBuilder newName = new StringBuilder(firstNames.get(random.nextInt(firstNameSize)));
            String name = patient.getSName();
            if (StringUtils.isBlank(name) || name.length() < 2) {
                newName.append(lastNameChar.get(random.nextInt(lastNameCharSize)));
                newName.append(lastNameChar.get(random.nextInt(lastNameCharSize)));
            } else if (name.length() == 2) {
                if (random.nextBoolean()) {
                    newName.append(lastNameChar.get(random.nextInt(lastNameCharSize)));
                } else {
                    newName.append(lastNameChar.get(random.nextInt(lastNameCharSize)));
                    newName.append(lastNameChar.get(random.nextInt(lastNameCharSize)));
                }
            } else {
                String c = StringUtils.substring(name, 1, 2);
                int rand = random.nextInt(4);
                if (rand == 0) {
                    newName.append(c);
                    newName.append(lastNameChar.get(random.nextInt(lastNameCharSize)));
                } else if (rand == 1) {
                    newName.append(lastNameChar.get(random.nextInt(lastNameCharSize)));
                    newName.append(c);
                } else if (rand == 2) {
                    newName.append(lastNameChar.get(random.nextInt(lastNameCharSize)));
                    newName.append(lastNameChar.get(random.nextInt(lastNameCharSize)));
                } else {
                    newName.append(lastNameChar.get(random.nextInt(lastNameCharSize)));
                }
            }

            patient.setNPatId(DateUtil.nowTimeMillis());
            patient.setSName(newName.toString());
            patient.setSEName(newName.toString());
            patient.setSPatNameInFile(null);
            String idcard = patient.getSIdNumber();
            if (StringUtils.isNotBlank(idcard)) {
                if (idcard.length() == 18) {
                    StringBuilder newIdcard = new StringBuilder();
                    newIdcard.append(idcard, 0, 10);
                    int month = random.nextInt(12) + 1;
                    newIdcard.append(month <  10 ? "0" + month : month);
                    int day = random.nextInt(27) + 1;
                    newIdcard.append(day < 10 ? "0" + day : day);
                    newIdcard.append(UUIDUitl.generateNum(2));
                    newIdcard.append(idcard.substring(16));
                    patient.setSIdNumber(newIdcard.toString());
                }
            }

            if (StringUtils.isNotBlank(patient.getSPhoneTel())) {
                StringBuilder phone = new StringBuilder(PHONE_PREFIX.get(random.nextInt(phonePrefixSize)));
                phone.append(UUIDUitl.generateNum(8));
                patient.setSPhoneTel(phone.toString());
            }

            patientGenDao.insert(patient);
            Long patid = patient.getNPatId();

            recBegin = recBegin.plusSeconds(random.nextInt(600));
            if (recBegin.isAfter(tomorrow)) {
                recBegin = recBegin.minusHours(16);
            }
            StudyEntity study = new StudyEntity();
            BeanUtils.copyProperties(resp, study);
            study.setNStuId(DateUtil.nowTimeMillis());
            study.setNPatID(patid);
            recBegin = recBegin.plusSeconds(random.nextInt(600));
            study.setRecordDate(recBegin);
            recBegin = recBegin.plusSeconds(random.nextInt(600));
            study.setStuDate(recBegin);
            recBegin = recBegin.plusSeconds(1200 + random.nextInt(600));
            study.setImageRecvDate(recBegin);
            recBegin = recBegin.plusSeconds(random.nextInt(600));
            study.setBurnDate(recBegin);

            studyGenDao.insert(study);

            ReportEntity report = reportService.findByStudyId(resp.getnStuId());
            if (report != null) {
                ReportEntity newReport = new ReportEntity();
                BeanUtils.copyProperties(report, newReport);
                newReport.setNReportId(null);
                newReport.setNStuId(study.getNStuId());
                newReport.setRepDate(recBegin.minusSeconds(600));
                newReport.setAudDate(recBegin.minusSeconds(300));
                newReport.setPrintDate(recBegin);
                reportGenDao.insert(newReport);
            }

        }

    }

    @Override
    public int getCtmr(LocalDateTime begin, LocalDateTime end) {
        return studyGenDao.getCtmr(begin, end);
    }

    @Override
    public int getHospCtmr(LocalDateTime begin, LocalDateTime end) {
        return studyGenDao.getHospCtmr(begin, end);
    }

    @Override
    public int getUs(LocalDateTime begin, LocalDateTime end) {
        return studyGenDao.getUs(begin, end);
    }

    @Override
    public int getHospUs(LocalDateTime begin, LocalDateTime end) {
        return studyGenDao.getHospUs(begin, end);
    }

    @Override
    public int getEs(LocalDateTime begin, LocalDateTime end) {
        return studyGenDao.getEs(begin, end);
    }

    @Override
    public int getHospEs(LocalDateTime begin, LocalDateTime end) {
        return studyGenDao.getHospEs(begin, end);
    }

    @Override
    public int getJy(LocalDateTime begin, LocalDateTime end) {
        return studyGenDao.getJy(begin, end);
    }

    @Override
    public List<UserPatientDTO> getStudyCollectGroup(StudyCollectGroupReq req) {
        return studyGenDao.getStudyCollectGroup(req);
    }

}
